import base64
import hmac
import random
import time
from typing import Optional, Union

import requests

from . import config


class AKSKSigner:
    """
    AKSK鉴权签名器，添加鉴权需要的请求标头
    """
    def __init__(
            self,
            conf: config.Config,
            nonce: Optional[int] = None,
            timestamp: Optional[str] = None,
            action: Optional[str] = None,
            region: Optional[str] = None,
            signature: Optional[str] = None,
            registered: str = "1",
    ):
        """
        :param nonce: 此参数参与签名计算。随机正整数。SDK 默认自动随机生成。
        :param timestamp: 此参数参与签名计算。当前 UNIX 时间戳，可记录发起 API 请求的时间。SDK 默认当前时间戳。例如1529223702，单位为秒。
                          注意：如果与服务器时间相差超过5分钟，会引起签名过期错误。
        :param action: 操作的接口名称。注意：某些接口不需要传递该参数，接口文档中会对此特别说明，此时即使传递该参数也不会生效。
        :param region: 地域参数，用来标识希望操作哪个地域的数据。
                        注意：某些接口不需要传递该参数，接口文档中会对此特别说明，此时即使传递该参数也不会生效。
        :param signature: 放置由官网的签名方法产生的签名。SDK 默认自动生成，也可替换为自己生成符合标准的签名。
        :param registered: 启用账户通讯录，传入值必须为1，创建的会议可出现在用户的会议列表中。
        """
        self.__config = conf
        self.nonce: Optional[int] = nonce
        self.timestamp: Optional[str] = timestamp
        self.action: Optional[str] = action
        self.region: Optional[str] = region
        self.signature: Optional[str] = signature
        self.registered: str = registered

    def add_auth_headers(self, http_req: requests.PreparedRequest) -> None:
        headers = self.build_auth_headers(method=http_req.method, path_url=http_req.path_url, body=http_req.body)
        http_req.headers.update(headers)

    def build_signature(self, method: str, path_url: str, body: Union[bytes, str]) -> str:
        """
           生成签名
        :return: 签名，需要设置在请求头X-TC-Signature中
        """

        if isinstance(body, (bytes, str)):
            req_body = body.decode("utf-8")
        else:
            req_body = ""

        sign_str = "{0}\nX-TC-Key={1}&X-TC-Nonce={2}&X-TC-Timestamp={3}\n{4}\n{5}".format(
            method.upper(), self.__config.secret_id, self.nonce, self.timestamp, path_url, req_body)
        signer = hmac.new(
            key=self.__config.secret_key.encode('utf-8'),
            msg=sign_str.encode('utf-8'), digestmod='sha256'
        )
        sign = signer.hexdigest()
        return base64.b64encode(sign.encode('utf-8')).decode('utf-8')

    def build_auth_headers(self, method: str, path_url: str, body: Union[bytes, str]) -> dict:
        # Content-Type
        headers = {config.HTTP_HEADER_CONTENT_TYPE: config.DEFAULT_CONTENT_TYPE}

        if self.__config.secret_id is None or self.__config.secret_id == "":
            raise Exception("AKSK签名错误，应用凭证“SecretId”不能为空。")

        headers[config.XTC_HEADER_KEY] = self.__config.secret_id

        if self.__config.app_id is None or self.__config.app_id == "":
            raise Exception("AKSK签名错误，企业ID“AppId”不能为空。")

        headers[config.HEADER_APPID] = self.__config.app_id
        headers[config.HEADER_SDKID] = self.__config.sdk_id

        if self.nonce is None or self.nonce == 0:
            ts = int(time.time() * 1000000)
            ri = int(random.randint(0, 999999))
            self.nonce = ts + ri

        headers[config.XTC_HEADER_NONCE] = str(self.nonce)

        if self.registered is not None or self.registered != "":
            headers[config.XTC_HEADER_REGISTERED] = self.registered
        else:
            headers[config.XTC_HEADER_REGISTERED] = "1"

        if self.__config.version is not None and self.__config.version != "":
            headers[config.XTC_HEADER_VERSION] = self.__config.version

        if self.action is not None and self.action != "":
            headers[config.XTC_HEADER_ACTION] = self.action

        if self.region is not None and self.region != "":
            headers[config.XTC_HEADER_REGION] = self.region

        if self.timestamp is None or self.timestamp == "":
            self.timestamp = str(int(time.time()))

        headers[config.XTC_HEADER_TIMESTAMP] = self.timestamp

        if self.signature is None or self.signature == "":
            self.signature = self.build_signature(method=method, path_url=path_url, body=body)

        headers[config.XTC_HEADER_SIGNATURE] = self.signature
        return headers
